package com.atguigu.schedule.service.impl;

import com.atguigu.schedule.pojo.RecordingResponse;
import com.atguigu.schedule.pojo.StartRecordingRequest;
import com.atguigu.schedule.service.FFmpegRecordingService;
import com.atguigu.schedule.service.FileConversionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * FFmpeg录制服务实现
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class FFmpegRecordingServiceImpl implements FFmpegRecordingService {
    
    @Value("${recording.output-path:/usr/local/srs/srs/trunk/objs/nginx/html/recordings/live/livestream}")
    private String outputPath;
    
    @Value("${recording.format:mp4}")
    private String defaultFormat;
    
    @Value("${srs.server-url:http://8.130.109.229:1985}")
    private String srsServerUrl;
    
    @Value("${srs.http-port:8080}")
    private int srsHttpPort;
    
    private final FileConversionService fileConversionService;
    
    // 存储录制进程
    private final Map<Long, Process> recordingProcesses = new ConcurrentHashMap<>();
    
    @Override
    public RecordingResponse startRecording(StartRecordingRequest request) {
        try {
            // 检查FFmpeg是否可用
            if (!isFFmpegAvailable()) {
                log.error("FFmpeg不可用，无法开始录制");
                return null;
            }
            
            // 生成录制ID
            Long recordingId = System.currentTimeMillis();
            
            // 生成文件路径（使用时间戳格式，便于按日期查询）
            LocalDateTime now = LocalDateTime.now();
            String dateStr = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            String timeStr = now.format(DateTimeFormatter.ofPattern("HHmmss"));
            String fileName = String.format("%s_%s_%s.%s", 
                dateStr,
                timeStr,
                recordingId,
                request.getFormat() != null ? request.getFormat() : defaultFormat
            );
            String filePath = outputPath + File.separator + fileName;
            
            // 构建FFmpeg命令
            String ffmpegCommand = buildFFmpegCommand(request, filePath);
            log.info("执行FFmpeg命令: {}", ffmpegCommand);
            
            // 启动FFmpeg进程
            ProcessBuilder processBuilder = new ProcessBuilder();
            // 根据操作系统选择命令
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                processBuilder.command("cmd", "/c", ffmpegCommand);
            } else {
                processBuilder.command("sh", "-c", ffmpegCommand);
            }
            processBuilder.directory(new File(System.getProperty("user.dir")));
            // 重定向错误输出到标准输出，方便调试
            processBuilder.redirectErrorStream(true);
            
            Process process = processBuilder.start();
            
            // 异步读取FFmpeg输出，避免阻塞
            final Process finalProcess = process;
            Thread outputReader = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(finalProcess.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        // 只记录错误和警告信息
                        if (line.contains("error") || line.contains("Error") || 
                            line.contains("ERROR") || line.contains("failed") || 
                            line.contains("Failed") || line.contains("WARNING")) {
                            log.warn("FFmpeg输出: {}", line);
                        }
                    }
                } catch (Exception e) {
                    log.warn("读取FFmpeg输出失败: {}", e.getMessage());
                }
            });
            outputReader.setDaemon(true);
            outputReader.start();
            
            // 检查进程是否立即失败
            Thread.sleep(1000); // 等待1秒检查进程状态
            if (!process.isAlive()) {
                int exitCode = process.exitValue();
                log.error("FFmpeg进程立即退出，退出码: {}", exitCode);
                // 尝试读取错误信息
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getErrorStream()))) {
                    StringBuilder errorOutput = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        errorOutput.append(line).append("\n");
                    }
                    if (errorOutput.length() > 0) {
                        log.error("FFmpeg错误输出: {}", errorOutput.toString());
                    }
                } catch (Exception e) {
                    log.warn("读取FFmpeg错误输出失败: {}", e.getMessage());
                }
                return null;
            }
            
            recordingProcesses.put(recordingId, process);
            
            // 创建录制响应
            RecordingResponse response = new RecordingResponse();
            response.setId(recordingId);
            response.setStreamName(request.getStreamName());
            response.setDescription(request.getDescription());
            response.setStatus(com.atguigu.schedule.pojo.RecordingStatus.RECORDING);
            response.setStartTime(LocalDateTime.now());
            response.setDuration(request.getDuration());
            response.setFilePath(filePath);
            response.setFormat(request.getFormat() != null ? request.getFormat() : defaultFormat);
            
            log.info("FFmpeg录制启动成功，录制ID: {}, 文件路径: {}", recordingId, filePath);
            return response;
            
        } catch (Exception e) {
            log.error("启动FFmpeg录制失败: {}", e.getMessage(), e);
            return null;
        }
    }
    
    @Override
    public boolean stopRecording(Long recordingId) {
        try {
            Process process = recordingProcesses.get(recordingId);
            if (process != null && process.isAlive()) {
                // 发送中断信号
                process.destroyForcibly();
                recordingProcesses.remove(recordingId);
                log.info("FFmpeg录制停止成功，录制ID: {}", recordingId);
                
                // 等待一段时间确保文件写入完成
                Thread.sleep(2000);
                
                // 检查并修复文件格式
                String fixedPath = fileConversionService.fixRecordingFileFormat(recordingId);
                if (fixedPath != null) {
                    log.info("录制文件格式修复成功: {}", fixedPath);
                } else {
                    log.warn("未找到需要修复的录制文件，录制ID: {}", recordingId);
                }
                
                return true;
            } else {
                log.warn("录制进程不存在或已停止，录制ID: {}", recordingId);
                return false;
            }
        } catch (Exception e) {
            log.error("停止FFmpeg录制失败: {}", e.getMessage(), e);
            return false;
        }
    }
    
    @Override
    public boolean isFFmpegAvailable() {
        try {
            ProcessBuilder processBuilder = new ProcessBuilder("ffmpeg", "-version");
            Process process = processBuilder.start();
            int exitCode = process.waitFor();
            return exitCode == 0;
        } catch (Exception e) {
            log.error("检查FFmpeg可用性失败: {}", e.getMessage());
            return false;
        }
    }
    
    @Override
    public Map<String, Object> getRecordingProcessStatus(Long recordingId) {
        Map<String, Object> status = new HashMap<>();
        Process process = recordingProcesses.get(recordingId);
        
        if (process != null) {
            status.put("isAlive", process.isAlive());
            status.put("pid", process.pid());
        } else {
            status.put("isAlive", false);
            status.put("pid", null);
        }
        
        return status;
    }
    
    /**
     * 构建FFmpeg命令
     */
    private String buildFFmpegCommand(StartRecordingRequest request, String outputPath) {
        StringBuilder command = new StringBuilder();
        command.append("ffmpeg ");
        // 添加超时和重试参数，确保能够连接到流
        command.append("-rtsp_transport tcp ");  // 使用TCP传输（如果适用）
        command.append("-reconnect 1 ");         // 自动重连
        command.append("-reconnect_at_eof 1 ");  // EOF时重连
        command.append("-reconnect_streamed 1 "); // 流式重连
        command.append("-reconnect_delay_max 2 "); // 最大重连延迟2秒
        command.append("-rw_timeout 5000000 ");  // 读写超时5秒（微秒）
        
        // 输入源 - 从SRS服务器拉流
        // SRS会将WebRTC流自动转换为HTTP-FLV格式
        // 从srsServerUrl中提取IP地址（格式：http://8.130.109.229:1985）
        String srsHost = srsServerUrl.replace("http://", "").replace(":1985", "");
        // 使用HTTP-FLV格式拉流（SRS会自动将WebRTC流转换为FLV格式）
        // 流地址格式：http://8.130.109.229:8080/live/livestream.flv
        String inputUrl = String.format("http://%s:%d/live/%s.flv", srsHost, srsHttpPort, request.getStreamName());
        log.info("使用拉流地址: {} (原始WebRTC流: webrtc://{}/live/{})", inputUrl, srsHost, request.getStreamName());
        command.append("-i \"").append(inputUrl).append("\" ");
        
        // 输出参数
        command.append("-c:v libx264 ");  // 视频编码
        command.append("-c:a aac ");      // 音频编码
        command.append("-preset fast ");  // 编码速度
        command.append("-crf 23 ");       // 质量参数
        command.append("-f mp4 ");        // 输出格式
        command.append("-y ");            // 覆盖已存在的文件
        
        // 录制时长
        if (request.getDuration() != null && request.getDuration() > 0) {
            command.append("-t ").append(request.getDuration()).append(" ");
        }
        
        // 确保输出目录存在
        File outputFile = new File(outputPath);
        File outputDir = outputFile.getParentFile();
        if (outputDir != null && !outputDir.exists()) {
            outputDir.mkdirs();
        }
        
        // 输出文件
        command.append("\"").append(outputPath).append("\"");
        
        return command.toString();
    }
}
